package structs;

import java.util.ArrayList;
import java.util.List;


import interfaces.ICard;
import interfaces.IPlayer;
import interfaces.Strategy;

public class MinimaxStrategy implements Strategy {

	private Game game;
	private Tree<MMNode> tree;

	//Current Hand
	private ICard hand1;
	private ICard hand2;
	private ICard hand3;

	//Deck
	private ArrayList<ICard> highLifeCards;
	private ArrayList<ICard> lowLifeCards;
	private ArrayList<ICard> highCards;
	private ArrayList<ICard> lowCards;
	private ArrayList<ICard> suit1HighCards;
	private ArrayList<ICard> suit1LowCards;
	private ArrayList<ICard> suit1ElseCards;
	private ArrayList<ICard> suit2HighCards;
	private ArrayList<ICard> suit2LowCards;
	private ArrayList<ICard> suit2ElseCards;
	private ArrayList<ICard> suit3HighCards;
	private ArrayList<ICard> suit3LowCards;
	private ArrayList<ICard> suit3ElseCards;

	public MinimaxStrategy(Game game){
		this.game = game;
	}

	public ICard nextMove() {
		this.setup();

		return this.decide();
	}

	private void setup(){
		this.tree = new Tree<MMNode>();
		this.tree.setRootElement(new Node<MMNode>(new MMNode(0,null,1)));

		this.highLifeCards = new ArrayList<ICard>();
		this.lowLifeCards = new ArrayList<ICard>();
		this.highCards = new ArrayList<ICard>();
		this.lowCards = new ArrayList<ICard>();

		this.suit1HighCards = new ArrayList<ICard>();
		this.suit1LowCards = new ArrayList<ICard>();
		this.suit1ElseCards = new ArrayList<ICard>();
		this.suit2HighCards = new ArrayList<ICard>();
		this.suit2LowCards = new ArrayList<ICard>();
		this.suit2ElseCards = new ArrayList<ICard>();
		this.suit3HighCards = new ArrayList<ICard>();
		this.suit3LowCards = new ArrayList<ICard>();
		this.suit3ElseCards = new ArrayList<ICard>();

		Node<MMNode> root = this.tree.getRootElement();
		IPlayer ai = this.game.getAIPlayer();
		Deck deck = this.game.getDeck();

		//		IPlayer opponent = null;
		//
		//		for(IPlayer player : this.game.getPlayers()){
		//			if(player != ai){
		//				opponent = player;
		//			}
		//		}

		//acting

		for(ICard card : deck.getCards()){
			if(card.getSuit().equalsIgnoreCase(deck.getLife().getSuit())){
				if(card.getPoints() > 3){
					this.highLifeCards.add(card);
				}
				else{
					this.lowLifeCards.add(card);
				}	
			}
			else{
				if(card.getPoints() > 3){
					this.highCards.add(card);
				}
				else{
					this.lowCards.add(card);
				}
			}
		}


		this.hand1 = ai.getCurrentHand().get(0);

		if(this.hand1 != null){
			for(ICard card : deck.getCards()){
				if(card.getSuit().equalsIgnoreCase(this.hand1.getSuit()))
				{
					if(card.getPoints() > this.hand1.getPoints())
					{
						this.suit1HighCards.add(card);
					}
					else{
						this.suit1LowCards.add(card);
					}	
				}
				
				else
				{
					this.suit1ElseCards.add(card);
				}
			}
		}



		this.hand2 = ai.getCurrentHand().get(1);

		if(this.hand2 != null){
			for(ICard card : deck.getCards()){
				if(card.getSuit().equalsIgnoreCase(this.hand2.getSuit())){
					if(card.getPoints() > this.hand2.getPoints()){
						this.suit2HighCards.add(card);
					}
					else{
						this.suit2LowCards.add(card);
					}	
				}
				else{
					this.suit2ElseCards.add(card);
				}
			}
		}

		this.hand3 = ai.getCurrentHand().get(2);
		if(this.hand3 != null){
			for(ICard card : deck.getCards()){
				if(card.getSuit().equalsIgnoreCase(this.hand3.getSuit())){
					if(card.getPoints() > this.hand3.getPoints()){
						this.suit3HighCards.add(card);
					}
					else{
						this.suit3LowCards.add(card);
					}	
				}
				else{
					this.suit3ElseCards.add(card);
				}
			}
		}

		Node<MMNode> hand1 = null;
		if(this.hand1 != null){
			hand1 = new Node<MMNode>(new MMNode(0,ai.getCurrentHand().get(0),1));
			root.addChild(hand1);
		}
		Node<MMNode> hand2 = null;
		if(this.hand2 != null){
			hand2 = new Node<MMNode>(new MMNode(0,ai.getCurrentHand().get(1),1));
			root.addChild(hand2);
		}
		Node<MMNode> hand3 = null;
		if(this.hand3 != null){
			hand3 = new Node<MMNode>(new MMNode(0,ai.getCurrentHand().get(2),1));
			root.addChild(hand3);
		}


		//Values according to the hand played by opponent
		//Suit high = 4 
		//Suit low = 1
		//Suit else = 2

		//Life high = 5
		//Life low = 3


		//If hand is life dont add highLifeCards and lowLifeCards
		if(this.hand1 != null){
			hand1.addChild(new Node<MMNode>(new MMNode(4,null,(float) this.suit1HighCards.size()/deck.cardsLeft())));
			hand1.addChild(new Node<MMNode>(new MMNode(1,null,(float) this.suit1LowCards.size()/deck.cardsLeft())));
			hand1.addChild(new Node<MMNode>(new MMNode(2,null,(float) this.suit1ElseCards.size()/deck.cardsLeft())));
			if(!hand1.getData().getCard().getSuit().equalsIgnoreCase(deck.getLife().getSuit())){
				hand1.addChild(new Node<MMNode>(new MMNode(5,null,(float) this.highLifeCards.size()/deck.cardsLeft())));
				hand1.addChild(new Node<MMNode>(new MMNode(3,null,(float) this.lowLifeCards.size()/deck.cardsLeft())));
			}
		}
		if(this.hand2 != null){
			hand2.addChild(new Node<MMNode>(new MMNode(4,null,(float) this.suit2HighCards.size()/deck.cardsLeft())));
			hand2.addChild(new Node<MMNode>(new MMNode(1,null,(float) this.suit2LowCards.size()/deck.cardsLeft())));
			hand2.addChild(new Node<MMNode>(new MMNode(2,null,(float) this.suit2ElseCards.size()/deck.cardsLeft())));
			if(!hand2.getData().getCard().getSuit().equalsIgnoreCase(deck.getLife().getSuit())){
				hand2.addChild(new Node<MMNode>(new MMNode(5,null,(float) this.highLifeCards.size()/deck.cardsLeft())));
				hand2.addChild(new Node<MMNode>(new MMNode(3,null,(float) this.lowLifeCards.size()/deck.cardsLeft())));
			}
		}
		if(this.hand3 != null){
			hand3.addChild(new Node<MMNode>(new MMNode(4,null,(float) this.suit3HighCards.size()/deck.cardsLeft())));
			hand3.addChild(new Node<MMNode>(new MMNode(1,null,(float) this.suit3LowCards.size()/deck.cardsLeft())));
			hand3.addChild(new Node<MMNode>(new MMNode(2,null,(float) this.suit3ElseCards.size()/deck.cardsLeft())));
			if(!hand3.getData().getCard().getSuit().equalsIgnoreCase(deck.getLife().getSuit())){
				hand3.addChild(new Node<MMNode>(new MMNode(5,null,(float) this.highLifeCards.size()/deck.cardsLeft())));
				hand3.addChild(new Node<MMNode>(new MMNode(3,null,(float) this.lowLifeCards.size()/deck.cardsLeft())));
			}
		}




	}

	private ICard decide(){
		MMNode best = expectMinimax(this.tree.getRootElement(), 2);

		return best.getCard();
	}

	private Node<MMNode> getMax(List<Node<MMNode>> child){
		Node<MMNode> max = child.get(0);

		for(Node<MMNode> c : child){
			if(c.getData().getValue() > max.getData().getValue()){
				max = c;
			}
		}

		return max;
	}

	private Node<MMNode> getMin(List<Node<MMNode>> child){
		Node<MMNode> min = null;

		for(Node<MMNode> c : child){
			if(min == null && c.getData().getProb() > 0.0){
				min = c;
			}
			else if(c.getData().getProb() > 0.0 && c.getData().getValue()*c.getData().getProb() < -min.getData().getValue()*min.getData().getProb()){
				min = c;
			}
		}

		return min;
	}

	private MMNode expectMinimax(Node<MMNode> node, int depth){

		//This is reacting
		List<Node<MMNode>> childs = node.getChildren();

		for(Node<MMNode> child : childs){			
			Node<MMNode> min = null;
			min = this.getMin(child.getChildren());
			child.getData().setValue( (float) min.getData().getValue() * min.getData().getProb() );
		}

		Node<MMNode> max = this.getMax(childs);		

		return max.getData();

	}

}
